#include <iostream>
#include <fstream>
#include <cstdlib>
#include <csignal>
#include <unistd.h>
#include <getopt.h>

using namespace std;

/* Funciones definidas por los generadores. */
extern void generarMejorCaso( ostream &salida );
extern void generarPeorCaso( ostream &salida );
extern void generarCasosAleatorios( ostream &salida );
extern bool establecerMaximo( const char * );
extern bool establecerMinimo( const char * );

static struct option gOpciones[] = {
	{ "aleatorio", 0, 0, 'a' },
	{ "peor", 0, 0, 'p' },
	{ "mejor", 0, 0, 'm' },
	{ "help", 0, 0, 'h' },
	{ "max", 1, 0, 's' },
	{ "min", 1, 0, 'i' },
	{ 0, 0, 0, 0 }
};

static void (*gFunciones[])(ostream &) = { generarMejorCaso, generarPeorCaso, generarCasosAleatorios };

static void ayuda( const char *nombre, ostream &salida ) {
	salida << "Uso: " << nombre << " [-hampsi] [archivo]\n"
		<< " * -h, --help:      Muestra este mensaje de ayuda.\n"
		<< " * -a, --aleatorio: Casos aleatorios.\n"
		<< " * -m, --mejor:     Mejores casos.\n"
		<< " * -p, --peor:      Peores casos.\n"
		<< " * -s, --max:       El tamaño máximo de los casos de prueba (dependiente del generador).\n"
		<< " * -i, --min:       El tamaño mínimo de los casos de prueba (dependiente del generador).\n"
		<< " archivo: El archivo donde guardar los datos (salida estándar si no se provee)."
		<< endl;
}

static void ctrlc( int ) {
	exit( 0 );
}

int main( int argc, char **argv ) {
	int op, caso = 2;
	const char *min = 0, *max = 0;
	opterr = 0;

	while ( (op = getopt_long( argc, argv, "hamps:i:", gOpciones, 0 )) != -1 ) {
		switch( op ) {
			case 'h': ayuda( argv[0], cout ); return 0;
			case 'a': caso = 2; break;
			case 'p': caso = 1; break;
			case 'm': caso = 0; break;
			case 's': max = optarg; break;
			case 'i': min = optarg; break;
			case '?':
			default: ayuda( argv[0], cerr ); return -1;
		}
	}

	if ( max && !establecerMaximo( max ) )
		return -1;

	if ( min && !establecerMinimo( min ) )
		return -1;

	signal( SIGINT, ctrlc );

	if ( optind >= 0 && optind < argc ) {
		ofstream salida( argv[optind] );
		if ( !salida.is_open() ) {
			cerr << "Error al abrir " << argv[ optind ] << " para escritura.\n";
			return -1;
		}
		gFunciones[ caso ]( salida );
	} else {
		gFunciones[ caso ]( cout );
	}

	return 0;
}
